﻿using System;
using System.Collections.Generic;
using System.Reflection;
using System.Linq;
using Bling.Util;
using Bling.DSL;

namespace Bling.DSL {
  using Bling.Ops;
  using System.Linq;
  public abstract partial class Expr : IExpr {
    public Expr Fold { get { return BaseFold; } }
    protected abstract Expr BaseFold { get; }
    public virtual Expr Cheap { get { return this; } }
  }

  public abstract partial class Expr<T> : Expr, IExpr<T> {
    public new Expr<T> Fold { get { return new DefaultTransformer().Transform(this); } }
    protected override Expr BaseFold { get { return Fold; } }
    public virtual Expr<T> Transform(Transformer t) { return this; }

    public Expr<S> Map<S>(Func<T, S> F, Func<S, T> G) {
      return new MapOperator<T, S>(F) { G = G }.Make(this);
    }
    public Expr<S> Map<S>(Func<T, S> F) {
      return (new Ops.MapOperator<T, S>(F)).Make(this);
    }
    public Expr<U> Map2<S,U>(Expr<S> Other, Func<T, S, U> F) {
      return (new Ops.MapOperator<T, S, U>(F)).Make(this, Other);
    }
    private string DumpT = null;
    public class DumpS {
      internal Expr<T> Outer;
      public string Look {
        get {
          if (Outer.DumpT != null) return Outer.DumpT;
          var eval = new ShareEval();
          eval.Go(Outer);
          eval.End();
          var output = new System.IO.StringWriter();
          eval.PrintShared(output);
          output.WriteLine(eval.Translate(Outer));
          Outer.DumpT = output.ToString();
          return Outer.DumpT;
        }
      }
    }
    public DumpS Dump { get { return new DumpS() { Outer = this }; } } 
  }
  public interface Transformer {
    Expr<T> Transform<T>(Expr<T> e);
  }
  public class DefaultTransformer : Transformer {
    public virtual Expr<T> Transform<T>(Expr<T> e) { return e.Transform(this); }
  }
  public abstract partial class Eval<EVAL> where EVAL : Eval<EVAL> {
    public virtual Info<S> Memo<S>(IExpr op) { return null; }
    public virtual Info<S> Memo<S>(IExpr op, Func<Info<S>> info, params Info[] args) { return info(); }

    public virtual Func<Info<S>, Info<T>> Operator<S, T>(Ops.Operation<S, T> op) { return null; }
    public virtual Func<Info<S>, Info<T>, Info<U>> Operator<S, T, U>(Ops.Operation<S, T, U> op) { return null; }
    public virtual Func<Info<S>, Info<T>, Info<U>, Info<V>> Operator<S, T, U, V>(Ops.Operation<S, T, U, V> op) { return null; }
    public virtual Func<Info<S>, Info<T>, Info<U>, Info<V>, Info<W>> Operator<S, T, U, V, W>(Ops.Operation<S, T, U, V, W> op) { return null; }




  }
  public abstract partial class TranslateEval<EVAL> : Eval<EVAL> where EVAL : Eval<EVAL> {
    // pass everything through
    public override Func<Info<S>, Info<T>> Operator<S, T>(Ops.Operation<S, T> op) {
      return s => new MyInfo<T>() { Value = op.Op.Make(((MyInfo<S>)s).Value) };
    }
    public override Func<Info<S>, Info<T>, Info<U>> Operator<S, T, U>(Ops.Operation<S, T, U> op) {
      return (s, t) => new MyInfo<U>() { Value = op.Op.Make(((MyInfo<S>)s).Value, ((MyInfo<T>)t).Value) };
    }
    public override Func<Info<S>, Info<T>, Info<U>, Info<V>> Operator<S, T, U, V>(Ops.Operation<S, T, U, V> op) {
      return (s, t, u) => new MyInfo<V>() { Value = op.Op.Make(((MyInfo<S>)s).Value, ((MyInfo<T>)t).Value, ((MyInfo<U>)u).Value) };
    }
    public override Func<Info<S>, Info<T>, Info<U>, Info<V>, Info<W>> Operator<S, T, U, V, W>(Ops.Operation<S, T, U, V, W> op) {
      return (s, t, u, v) => new MyInfo<W>() { Value = op.Op.Make(((MyInfo<S>)s).Value, ((MyInfo<T>)t).Value, ((MyInfo<U>)u).Value, ((MyInfo<V>)u).Value) };
    }
  }



  public interface IShareEval<EVAL> : IEval<EVAL> where EVAL : Eval<EVAL> {
    Eval<EVAL>.Info<T> Share<T>(ShareEval.ShareExpr<T> Shared);
  }

  public class ShareEval : TranslateEval<ShareEval> {
    private readonly DSL.ExprKeys Keys = new DSL.ExprKeys();
    private readonly Dictionary<ExprKey, IShareExpr> Shares = new Dictionary<ExprKey, IShareExpr>();
    private List<ExprKey> Order = new List<ExprKey>();
    public interface IShareExpr : IExpr, IProxyExpr {
      int Id { get; set;  }
      bool Shared { get; }
      Expr BaseUse { get;  set; }
      HashSet<IShareExpr> Depends { get; }
    }
    private HashSet<IShareExpr> Depends;
    public class ShareExpr<T> : ProxyExpr<T>, IShareExpr {
      public HashSet<IShareExpr> Depends { get;  private set; }
      public Expr<T> Use { get; private set; }
      public int Id { get; set; }
      public bool Shared { get; internal set; }
      public ShareExpr(int Id, Expr<T> Underlying) : base(Underlying) { 
        this.Id = Id; Use = Underlying;
        this.Depends = new HashSet<IShareExpr>();
      }

      protected override Expr<S> Make<S>(Expr<S> e) {
        throw new NotSupportedException();
        //return e; // cast off sharing.
        //if (e.Equals(Use)) return (ProxyExpr<S>) (object) this;
        //throw new NotSupportedException();
        //return new JoinExpr<T>(e);
      }
      public Expr BaseUse { get { return Use; }  set { Use = (Expr<T>)value; } }
      protected override string ToString1() {
        if (!Shared) return Use.ToString();
        else return "T" + Id;
      }
      protected override Eval<EVAL>.Info<T> Eval<EVAL>(Eval<EVAL> txt) {
        if (txt is ShareEval) {
          var Ds = ((ShareEval)(object)txt).Depends;
          if (Ds != null) Ds.Add(this);
          return (Eval<EVAL>.Info<T>)((ShareEval)(object)txt).NoTranslation(this);
        } else if (txt is TranslateBack)
          return (Eval<EVAL>.Info<T>)((TranslateBack)(object)txt).NoTranslation(this);
        else if (txt is IShareEval<EVAL>) {
          return ((IShareEval<EVAL>)txt).Share(this);
        } else if (txt is TranslateEval<EVAL>) {
          return txt.DoEval(Use);
        }
        return base.Eval<EVAL>(txt);
      }
      protected override int GetHashCode0() {
        return IdentityHashCode;
      }
      protected override bool Equals0(Expr<T> obj) {
        return object.ReferenceEquals(this, obj);
      }
    }
    private readonly Queue<ExprKey> Unprocessed = new Queue<ExprKey>();
    protected override Expr<T> RealTranslate<T>(Expr<T> value) {
      if (value is Operation<T> /*|| value is ICompositeT<T>*/) {
        var key = Keys[value];
        if (!AtEnd) {
          if (!Shares.ContainsKey(key)) {
            (Order.Count == Shares.Count).Assert();
            Unprocessed.Enqueue(key);
            Shares[key] = new ShareExpr<T>(Shares.Count, value);
            Order.Add(key);
          } else {
            ((ShareExpr<T>)Shares[key]).Shared = true;
          }
          return (ShareExpr<T>)Shares[key];
        } else if (Shares.ContainsKey(key)) {
          var ret = Shares[key];
          (ret.Shared).Assert();
          return (ShareExpr<T>) ret;
        }
      }
      value = base.RealTranslate<T>(value);
      return value;
    }
    public void Go<T>(Expr<T> Base) {
      var First = Translate(Base);
      while (Unprocessed.Count != 0) {
        var key = Unprocessed.Dequeue();
        var s = Shares[key];
        var e = s.BaseUnderlying;
        Depends = s.Depends;
        e = EvalWithoutTranslate(e);
        Depends = null;
        s.BaseUse = e;
      }
    }
    private bool AtEnd = false;
    public void End() {
      AtEnd = true;
      // eliminate unshared dependencies
      var TranslateBack = new TranslateBack();


      // sort based on dependencies. 
      {
        var NewOrder = new List<IShareExpr>();
        var RemoveKeys = new List<ExprKey>();
        {
          var Processed = new HashSet<IShareExpr>();
          Action<IShareExpr> Process = null;
          var ToRemove = new Queue<IShareExpr>();
          Process = (s) => {
            if (Processed.Contains(s)) return;
            foreach (var e in s.Depends) Process(e);
            Processed.Add(s);
            if (s.Shared) {
              s.BaseUse = TranslateBack.Translate(s.BaseUse);
              NewOrder.Add(s);
            } else {
              RemoveKeys.Add(Order[s.Id]);
            }

            ToRemove.Clear();
            foreach (var d in s.Depends) if (!d.Shared) ToRemove.Enqueue(d);
            foreach (var d in ToRemove) s.Depends.Remove(d);
            while (ToRemove.Count != 0) {
              var d = ToRemove.Dequeue();
              foreach (var dd in d.Depends)
                if (dd.Shared) s.Depends.Add(dd);
                else false.Assert();
            }

          };
          foreach (var e in Shares) Process(e.Value);
          foreach (var e in RemoveKeys) Shares.Remove(e);
        }


        var OldOrder = this.Order;
        this.Order = new List<ExprKey>();
        for (int i = 0; i < NewOrder.Count; i++) {
          (Order.Count == i).Assert();
          Order.Add(OldOrder[NewOrder[i].Id]);
          NewOrder[i].Id = i;
        }
      }
    }
    private class TranslateBack : TranslateEval<TranslateBack> {
      protected override Expr<T> RealTranslate<T>(Expr<T> value) {
        if ((value is ShareExpr<T>) && !((ShareExpr<T>)value).Shared) {
          return Translate(((ShareExpr<T>)value).Use);
        }
        return base.RealTranslate<T>(value);
      }
    }

    public List<IShareExpr> Shared {
      get {
        var ret = new List<IShareExpr>();
        for (int i = 0; i < Order.Count; i++) {
          var key = Order[i];
          var s = Shares[key];
          s.Shared.Assert();
          ret.Add(s);
        }
        return ret;
      }

    }

    public void PrintShared(System.IO.TextWriter output) {
      for (int i = 0; i < Order.Count; i++) {
        var key = Order[i];
        var s = Shares[key];
        if (!s.Shared) continue;
        output.WriteLine(s.TypeOfT.Name + " T" + s.Id + " = " + s.BaseUse + ";");
      }
    }
  }
  public abstract partial class ScopedEval<EVAL> : Eval<EVAL>, IParameterEval<EVAL> where EVAL : Eval<EVAL> {
    public ScopedEval() {
      Top = new Scope() { Previous = null, Depth = 0 };
      ScopeFor = (args) => Top;
      ParamInScopeF = (x, y) => { };
      AtLeast = (x, y) => { };
    }
    public partial interface LikeScope {
      int Depth { get; }
      Info<S> Publish<S>(ScopedEval<EVAL> self, IExpr op, Info<S> info, params Info[] args);
    }
    public partial class Scope : LikeScope {
      internal Scope Previous;
      public int Depth { get; internal set; }
      internal readonly Dictionary<IExpr, Info> Temps = new Dictionary<IExpr, Info>();
      internal readonly List<IExpr> Order = new List<IExpr>();
      public Info<S> Publish<S>(ScopedEval<EVAL> self, IExpr op, Info<S> Code, params Info[] args) {
        return self.PublishToScope(this, op, Code, args);
      }
      public Scope Find(int Depth) {
        (Depth <= this.Depth).Assert();
        if (Depth == this.Depth) return this;
        return Previous.Find(Depth);
      }
      internal readonly Dictionary<IParameterExpr, Info> Params = new Dictionary<IParameterExpr, Eval<EVAL>.Info>();
    }
    public virtual Eval<EVAL>.Info<T> Parameter<T>(ParameterExpr<T> value) {
      var scope = Top;
      while (scope != null) {
        if (scope.Params.ContainsKey(value)) return (Info<T>)scope.Params[value];
        scope = scope.Previous;
      }
      return null; // not found
    }
    public Action<Scope, Info> ParamInScopeF { private get; set; }
    public Action<Info, Info> AtLeast { get; set; }

    public void PutParamInScope<T>(ParameterExpr<T> Param, Info<T> Info) {
      ParamInScopeF(Top, Info);
      Top.Params[Param] = Info;
    }

    protected abstract Info<S> AllocateTemp<S>(Scope scope, Info<S> Code);
    private readonly Dictionary<Info, Info> CodeMap = new Dictionary<Info, Info>();
    protected string TempName { get { return "t" + Top.Depth + "_" + Top.Order.Count; } }

    protected virtual Info<S> PublishToScope<S>(Scope scope, IExpr op, Info<S> Code, params Info[] args) {
      var AsTemp = AllocateTemp(scope, Code); // create a new info to hold the temp, the current info is the code for the temp.
      if (AsTemp == null) 
        return Code; // don't allocate temp!
      ParamInScopeF(scope, AsTemp);
      CodeMap[AsTemp] = Code;
      (!scope.Temps.ContainsKey(op)).Assert();
      scope.Temps[op] = AsTemp;
      scope.Order.Add(op);
      (scope.Order.Count == scope.Temps.Count).Assert();
      
      return AsTemp;
    }
    /*
    public override Eval<EVAL>.Info<K> Access<K, T, ARITY>(Eval<EVAL>.Info<T> info, int Idx) {
      var ret = base.Access<K, T, ARITY>(info, Idx);
      AtLeast(ret, info);
      return ret;
    }
    public override Eval<EVAL>.Info<T> Composite<K, T, ARITY>(Composite<K,T> Expr, params Eval<EVAL>.Info<K>[] info) {
      var ret = base.Composite<K, T, ARITY>(Expr, info);
      foreach (var i in info) AtLeast(ret, i);
      return ret;
    }*/

    public Scope Top { get; private set; }
    public virtual void PushScope() {
      Top = new Scope() { Previous = Top, Depth = Top.Depth + 1 };
    }
    public bool IsEmpty { get { return Top == null; } }
    protected Scope TopScope { get { return Top; } }
    public struct Temp {
      //public object Op;
      public Info AsTemp;
      public Info Code;
    }
    public virtual IList<Temp> PopScope() {
      var array = new Temp[Top.Order.Count];
      (Top.Order.Count == Top.Temps.Count).Assert();
      for (int i = 0; i < Top.Order.Count; i++) {
        array[i] = new Temp() {
          //Op = Top.Order[i],
          AsTemp = Top.Temps[Top.Order[i]],
          Code = CodeMap[Top.Temps[Top.Order[i]]],
        };
      }
      Top = Top.Previous;
      return array;
    }
    public override Info<S> Memo<S>(IExpr op) {
      // look up.
      for (var t = Top; t != null; t = t.Previous)
        if (t.Temps.ContainsKey(op)) return (Info<S>)t.Temps[op];
      return null;
    }
    public Func<Info[], Scope> ScopeFor { set; private get; }

    //protected virtual Scope ScopeFor<S>(Operation<S> op, Info<S> Temp, params Eval<EVAL>.Info[] args) { return Top; }
    public override Info<S> Memo<S>(IExpr op, Func<Info<S>> Code, params Eval<EVAL>.Info[] args) {
      var Code0 = base.Memo(op, Code, args);
      var scope = ScopeFor(args);
      (Memo<S>(op) == null).Assert();
      (!(scope.Temps.ContainsKey(op))).Assert();
      return scope.Publish(this, op, Code0, args);
    }
    // push a scope for true, eval ifTrue in that scope, 
  }
  public interface ITableEval<EVAL> where EVAL : Eval<EVAL> {
    Eval<EVAL>.Info<T> Table<T>(Expr<T>[] Values, Expr<int> Index);
    Eval<EVAL>.Info<T> Table<T>(T[] Values, Expr<int> Index);
    Eval<EVAL>.Info<T> Block<T>(T[] Values, Eval<EVAL>.Info<int> idx);
  }

}

namespace Bling.Ops {
  using Bling.Core;

  public interface IConditionEval<EVAL> : IEval<EVAL> where EVAL : Eval<EVAL> {
    Eval<EVAL>.Info<S> Condition<S>(Expr<bool> Test, Expr<S> IfTrue, Expr<S> IfFalse);
  }
  public interface IConditionEval2<EVAL> : IConditionEval<EVAL> where EVAL : Eval<EVAL> {
    Eval<EVAL>.Info<S> DCondition<S>(Expr<bool> Test, Expr<S> IfTrue, Expr<S> IfFalse);
  }

  public partial interface IOperator { }
  public partial interface IOperator<S> : IOperator {
    bool IsCheap { get; }
  }

  public partial interface IMemoOperator<S> : IOperator<S> { }

  public partial interface IBaseOperator<S,TDelegate> : IOperator<S> { }
  public partial interface IOperator<S, CNT> : IOperator<S> where CNT : IOperator<S, CNT> { }

  public partial interface IOperatorX<S, T> : IBaseOperator<T,Func<S,T>> {
    Expr<T> Expand(Expr<S> argA);
    Expr<S> Inverse(Expr<S> argA, Expr<T> result);
    SmartString Format(SmartString s);
    Expr<T> Make(Expr<S> ArgA);
    Eval<EVAL>.Info<T> Eval<EVAL>(Operation<S, T> op, Eval<EVAL> txt) where EVAL : Eval<EVAL>;
  }
  public partial interface IOperator<S, T, CNT> : IOperatorX<S,T>, IOperator<T, CNT> where CNT : IOperator<S,T,CNT> { }


  public partial interface OperatorX<S, T, U> : IBaseOperator<U, System.Func<S,T,U>> {
    Expr<U> Expand(Expr<S> argA, Expr<T> argB);
    Expr<S> InverseA(Expr<S> argA, Expr<T> argB, Expr<U> result);
    Expr<T> InverseB(Expr<S> argA, Expr<T> argB, Expr<U> result);
    SmartString Format(SmartString argA, SmartString argB);
    Eval<EVAL>.Info<U> Eval<EVAL>(Operation<S,T,U> op, Eval<EVAL> txt) where EVAL : Eval<EVAL>;
    Expr<U> Make(Expr<S> ArgA, Expr<T> ArgB);
  }
  public partial interface IOperator<S, T, U, CNT> : OperatorX<S, T, U>, IOperator<U, CNT> where CNT : IOperator<S,T,U,CNT>  { }
  public partial interface OperatorX<S, T, U, V> : IBaseOperator<V, Func<S,T,U,V>> {
    Expr<V> Expand(Expr<S> argA, Expr<T> argB, Expr<U> argC);
    Expr<S> InverseA(Expr<S> argA, Expr<T> argB, Expr<U> argC, Expr<V> result);
    Expr<T> InverseB(Expr<S> argA, Expr<T> argB, Expr<U> argC, Expr<V> result);
    Expr<U> InverseC(Expr<S> argA, Expr<T> argB, Expr<U> argC, Expr<V> result);
    SmartString Format(SmartString argA, SmartString argB, SmartString argC);
    Expr<V> Make(Expr<S> ArgA, Expr<T> ArgB, Expr<U> ArgC);
    Eval<EVAL>.Info<V> Eval<EVAL>(Operation<S, T, U, V> op, Eval<EVAL> txt) where EVAL : Eval<EVAL>;
  }
  public partial interface OperatorX<S, T, U, V, W> : IBaseOperator<W, Func<S,T,U,V,W>> {
    Expr<W> Expand(Expr<S> argA, Expr<T> argB, Expr<U> argC, Expr<V> argD);
    Expr<S> InverseA(Expr<S> argA, Expr<T> argB, Expr<U> argC, Expr<V> argD, Expr<W> result);
    Expr<T> InverseB(Expr<S> argA, Expr<T> argB, Expr<U> argC, Expr<V> argD, Expr<W> result);
    Expr<U> InverseC(Expr<S> argA, Expr<T> argB, Expr<U> argC, Expr<V> argD, Expr<W> result);
    Expr<V> InverseD(Expr<S> argA, Expr<T> argB, Expr<U> argC, Expr<V> argD, Expr<W> result);
    SmartString Format(SmartString argA, SmartString argB, SmartString argC, SmartString argD);
    Expr<W> Make(Expr<S> ArgA, Expr<T> ArgB, Expr<U> ArgC, Expr<V> ArgD);
    Eval<EVAL>.Info<W> Eval<EVAL>(Operation<S, T, U, V, W> op, Eval<EVAL> txt) where EVAL : Eval<EVAL>;
  }
  public partial interface IOperator<S, T, U, V, CNT>    : OperatorX<S, T, U, V   >, IOperator<V, CNT> where CNT : IOperator<S,T,U,V,CNT  > { }
  public partial interface IOperator<S, T, U, V, W, CNT> : OperatorX<S, T, U, V, W>, IOperator<W, CNT> where CNT : IOperator<S,T,U,V,W,CNT> { }

  public partial class BaseOperatorX<S> : IOperator<S> {
    public bool CanOptimize { get; set; }
    public BaseOperatorX() { CanOptimize = true; }
    public virtual bool IsCheap { get { return false; } }
  }

  public partial class BaseOperatorX<S, T> : BaseOperatorX<T>, IOperatorX<S, T> {
    public virtual Expr<T> Expand(Expr<S> argA) { return null; }
    public virtual Expr<S> Inverse(Expr<S> argA, Expr<T> result) { return null; }
    public virtual SmartString Format(SmartString s) { return "(" + s + ")"; }
    public virtual Expr<T> Make(Expr<S> ArgA) {
      if (ArgA.IsObviousConstant && AsFunc != null && CanOptimize) {
        var argAValue = ArgA.CurrentValue;
        return new Constant<T>(AsFunc(argAValue));
      }
      return new Operation<S, T>() { ArgA = ArgA, Op = this }; 
    }
    public virtual Eval<EVAL>.Info<T> Eval<EVAL>(Operation<S, T> op, Eval<EVAL> txt) where EVAL : Eval<EVAL> {
      {
        var memo = txt.Memo<T>(op);
        if (memo != null) return memo;
      }
      // try to compute, otherwise...
      var f = txt.Operator<S, T>(op);
      if (f != null) {
        var argA = txt.DoEval(op.ArgA);


        return txt.Memo(op, () => f(argA), argA);
      }
      return txt.DoEval(op.Expand0);
      //return txt.Memo(this, Expand0.Eval(txt));
    }
  }
  public partial class Operator<S, T, CNT> : BaseOperatorX<S, T>, IOperator<S, T, CNT> where CNT : Operator<S,T,CNT> {
    public static CNT Instance { get; private set; }
    public Operator() { (Instance == null).Assert(); Instance = (CNT) this; }
  }
  public partial class BaseOperatorX<S, T, U> : BaseOperatorX<U>, OperatorX<S, T, U> {
    public virtual Expr<U> Expand(Expr<S> argA, Expr<T> argB) { return null; }
    public virtual Expr<S> InverseA(Expr<S> argA, Expr<T> argB, Expr<U> result) { return null; }
    public virtual Expr<T> InverseB(Expr<S> argA, Expr<T> argB, Expr<U> result) { return null; }
    public virtual SmartString Format(SmartString argA, SmartString argB) { return "(" + argA + ", " + argB + ")"; }
    public virtual Expr<U> Make(Expr<S> ArgA, Expr<T> ArgB) {
      if (ArgA.IsObviousConstant && ArgB.IsObviousConstant && AsFunc != null && CanOptimize) {
        var argAValue = ArgA.CurrentValue;
        var argBValue = ArgB.CurrentValue;
        return new Constant<U>(AsFunc(argAValue, argBValue));
      }
      return new Operation<S, T, U>() { ArgA = ArgA, ArgB = ArgB, Op = this };
    }
    public virtual Eval<EVAL>.Info<U> Eval<EVAL>(Operation<S, T,U> op, Eval<EVAL> txt) where EVAL : Eval<EVAL> {
      {
        var memo = txt.Memo<U>(op);
        if (memo != null) return memo;
      }
      var f = txt.Operator<S, T, U>(op);
      if (f != null) {
        var argA = txt.DoEval(op.ArgA);
        var argB = txt.DoEval(op.ArgB);
        return txt.Memo(op, () => f(argA, argB), argA, argB);
      }
      return txt.DoEval(op.Expand0);
      //return txt.Memo(this, Expand0.Eval(txt));
    }
  }
  public interface IMultiOp { }

  public partial class Operator<S, T, U, CNT> : BaseOperatorX<S, T, U>, IOperator<S, T, U, CNT> where CNT : Operator<S, T, U, CNT> {
    public static CNT Instance { get; private set; }
    public Operator() {
      if (this is IMultiOp) { } else {
        (Instance == null).Assert(); Instance = (CNT)this;
      }
    }
  }
  public partial class BaseOperatorX<S, T, U, V> : BaseOperatorX<V>, OperatorX<S, T, U, V> {
    public virtual Expr<V> Expand(Expr<S> argA, Expr<T> argB, Expr<U> argC) { return null; }
    public virtual Expr<S> InverseA(Expr<S> argA, Expr<T> argB, Expr<U> argC, Expr<V> result) { return null; }
    public virtual Expr<T> InverseB(Expr<S> argA, Expr<T> argB, Expr<U> argC, Expr<V> result) { return null; }
    public virtual Expr<U> InverseC(Expr<S> argA, Expr<T> argB, Expr<U> argC, Expr<V> result) { return null; }
    public virtual SmartString Format(SmartString argA, SmartString argB, SmartString argC) { return "(" + argA + ", " + argB + ", " + argC + ")"; }
    public Expr<V> Make(Expr<S> ArgA, Expr<T> ArgB, Expr<U> ArgC) {

      if (ArgA.IsObviousConstant && ArgB.IsObviousConstant && ArgC.IsObviousConstant && AsFunc != null && CanOptimize) {
        var argAValue = ArgA.CurrentValue;
        var argBValue = ArgB.CurrentValue;
        var argCValue = ArgC.CurrentValue;
        return new Constant<V>(AsFunc(argAValue, argBValue, argCValue));
      }
      return new Operation<S, T, U, V>() { ArgA = ArgA, ArgB = ArgB, ArgC = ArgC, Op = this };
    }
    public virtual Eval<EVAL>.Info<V> Eval<EVAL>(Operation<S, T, U, V> op, Eval<EVAL> txt) where EVAL : Eval<EVAL> {
      {
        var memo = txt.Memo<V>(op);
        if (memo != null) return memo;
      }
      return EvalCenter(op, txt);
    }
    protected virtual Eval<EVAL>.Info<V> EvalCenter<EVAL>(Operation<S, T, U, V> op, Eval<EVAL> txt) where EVAL : Eval<EVAL> {
      var f = txt.Operator<S, T, U, V>(op);
      if (f != null) {
        var argA = txt.DoEval(op.ArgA);
        var argB = txt.DoEval(op.ArgB);
        var argC = txt.DoEval(op.ArgC);
        return txt.Memo(op, () => f(argA, argB, argC), argA, argB, argC);
      }
      return txt.DoEval(op.Expand0);
    }
  }
  public partial class Operator<S, T, U, V, CNT> : BaseOperatorX<S, T, U, V>, IOperator<S, T, U, V, CNT> where CNT : Operator<S, T, U, V, CNT> {
    public static CNT Instance { get; private set; }
    public Operator() { (Instance == null).Assert(); Instance = (CNT) this; }
  }
  public partial class BaseOperatorX<S, T, U, V, W> : BaseOperatorX<W>, OperatorX<S, T, U, V, W> {
    public virtual Expr<W>   Expand(Expr<S> argA, Expr<T> argB, Expr<U> argC, Expr<V> argD) { return null; }
    public virtual Expr<S> InverseA(Expr<S> argA, Expr<T> argB, Expr<U> argC, Expr<V> argD, Expr<W> result) { return null; }
    public virtual Expr<T> InverseB(Expr<S> argA, Expr<T> argB, Expr<U> argC, Expr<V> argD, Expr<W> result) { return null; }
    public virtual Expr<U> InverseC(Expr<S> argA, Expr<T> argB, Expr<U> argC, Expr<V> argD, Expr<W> result) { return null; }
    public virtual Expr<V> InverseD(Expr<S> argA, Expr<T> argB, Expr<U> argC, Expr<V> argD, Expr<W> result) { return null; }
    public virtual SmartString Format(SmartString argA, SmartString argB, SmartString argC, SmartString argD) { return "(" + argA + ", " + argB + ", " + argC + ", " + argD + ")"; }
    public Expr<W> Make(Expr<S> ArgA, Expr<T> ArgB, Expr<U> ArgC, Expr<V> ArgD) {
      if (CanOptimize && ArgA is Constant<S> && ArgB is Constant<T> && ArgC is Constant<U> && ArgD is Constant<V> && AsFunc != null) {
        return new Constant<W>(AsFunc(((Constant<S>)ArgA).Value, ((Constant<T>)ArgB).Value, ((Constant<U>)ArgC).Value, ((Constant<V>)ArgD).Value));
      }
      return new Operation<S, T, U, V, W>() { ArgA = ArgA, ArgB = ArgB, ArgC = ArgC, ArgD = ArgD, Op = this };
    }
    public virtual Eval<EVAL>.Info<W> Eval<EVAL>(Operation<S, T, U, V, W> op, Eval<EVAL> txt) where EVAL : Eval<EVAL> {
      {
        var memo = txt.Memo<W>(op);
        if (memo != null) return memo;
      }
      var f = txt.Operator<S, T, U, V, W>(op);
      if (f != null) {
        var argA = txt.DoEval(op.ArgA);
        var argB = txt.DoEval(op.ArgB);
        var argC = txt.DoEval(op.ArgC);
        var argD = txt.DoEval(op.ArgD);
        return txt.Memo(op, () => f(argA, argB, argC, argD), argA, argB, argC, argD);
      }
      return txt.DoEval(op.Expand0);
    }
  }
  public partial class Operator<S, T, U, V, W, CNT> : BaseOperatorX<S, T, U, V, W>, IOperator<S, T, U, V, W, CNT> where CNT : Operator<S, T, U,V,W, CNT> {
    public static CNT Instance { get; private set; }
    public Operator() { (Instance == null).Assert(); Instance = (CNT) this; }
  }
  public partial class ProtectOperator<T> : BaseOperatorX<T, T> {
    public readonly Func<Expr<T>, Expr<T>> InverseF;
    public ProtectOperator(Func<Expr<T>, Expr<T>> F) {
      this.InverseF = F;
    }
    public override Expr<T> Expand(Expr<T> argA) { return argA; }
    public override Expr<T> Inverse(Expr<T> argA, Expr<T> result) {
      return InverseF(result);
    }
  }
  public interface IMapEval<EVAL> where EVAL : Eval<EVAL> {
    Eval<EVAL>.Info<T> Map<S, T>(Operation<S, T> op, Func<S, T> F);
    Eval<EVAL>.Info<U> Map<S, T, U>(Operation<S, T, U> op, Func<S, T, U> F);
  }
  public partial class MapOperator<S, T> : BaseOperatorX<S, T> {
    public readonly Func<S, T> F;
    public Func<T, S> G;
    public MapOperator(Func<S, T> F) {
      this.F = F;
    }
    public override int GetHashCode() {
      if (!CanOptimize) return base.GetHashCode();
      return F.GetHashCode() + (G == null ? 0 : G.GetHashCode());
    }
    public override bool Equals(object obj) {
      if (!CanOptimize) return base.Equals(obj);
      if (obj is MapOperator<S, T>) {
        return F == ((MapOperator<S, T>)obj).F && G == ((MapOperator<S, T>)obj).G;
      }
      return base.Equals(obj);
    }
    public override Eval<EVAL>.Info<T> Eval<EVAL>(Operation<S, T> op, Eval<EVAL> txt) {
      if (txt is IMapEval<EVAL>) return ((IMapEval<EVAL>)txt).Map<S, T>(op, F);
      return base.Eval<EVAL>(op, txt);
    }

    public override SmartString Format(SmartString s) {
      return "Map" + base.Format(s);
    }
    public override Expr<S> Inverse(Expr<S> argA, Expr<T> result) {
      if (G == null) return base.Inverse(argA, result);
      return new MapOperator<T, S>(G) { G = F }.Make(result);
    }
  }
  public partial class MapOperator<S, T, U> : BaseOperatorX<S, T, U> {
    public readonly Func<S, T, U> STU;                      // u = s + t
    public virtual Func<S, U, T> SUT { private get; set; }
    public virtual Func<T, S, U> TSU { private get; set; }
    public virtual Func<T, U, S> TUS { private get; set; }  // s = -t + u
    public virtual Func<U, T, S> UTS { private get; set; }  // s = u - t
    public virtual Func<U, S, T> UST { private get; set; }  // t = u - s
    public MapOperator(Func<S, T, U> F) {
      this.STU = F;
    }
    public override int GetHashCode() {
      if (!CanOptimize) return base.GetHashCode();
      return STU.GetHashCode();
    }
    public override bool Equals(object obj) {
      if (!CanOptimize) return base.Equals(obj);
      if (obj is MapOperator<S, T,U>) {
        return STU == ((MapOperator<S, T,U>)obj).STU;
      }
      return base.Equals(obj);
    }
    public override Eval<EVAL>.Info<U> Eval<EVAL>(Operation<S, T, U> op, Eval<EVAL> txt) {
      if (txt is IMapEval<EVAL>) return ((IMapEval<EVAL>)txt).Map<S, T,U>(op, STU);
      return base.Eval<EVAL>(op, txt);
    }
    public override SmartString Format(SmartString s, SmartString t) {
      return "Map" + base.Format(s, t);
    }
    // given U and T
    public override Expr<S> InverseA(Expr<S> argA, Expr<T> argB, Expr<U> result) {
      if (UTS != null)
        return new MapOperator<U, T, S>(UTS) { UTS = STU, UST = SUT, SUT = UST, TUS = TSU, TSU = TUS }.Make(result, argB);
      if (TUS != null)
        return new MapOperator<T, U, S>(TUS) { UTS = SUT, UST = STU, SUT = TSU, TUS = UST, TSU = UTS }.Make(argB, result);

      return base.InverseA(argA, argB, result);

    }
    public override Expr<T> InverseB(Expr<S> argA, Expr<T> argB, Expr<U> result) {
      if (UST != null) 
        return new MapOperator<U, S, T>(UST) { UTS = TSU, UST = TUS, SUT = UTS, TUS = STU, TSU = SUT }.Make(result, argA);
      if (SUT != null)
        return new MapOperator<S, U, T>(SUT) { UTS = TUS, UST = TSU, SUT = STU, TUS = UTS, TSU = UST }.Make(argA, result);

      return base.InverseB(argA, argB, result);
    }
  }
  // direct coercion.
  public abstract partial class CastOperator<S, T, CNT> : Operator<S, T, CNT> where CNT : CastOperator<S,T,CNT> {
    public override SmartString Format(SmartString s) {
      return "To" + typeof(T).Name + base.Format(s);
    }
  }
  public partial class DownCastOperator<S, T> : CastOperator<S, T, DownCastOperator<S, T>> where T : S {
    private DownCastOperator() {}
    public new static readonly DownCastOperator<S,T> Instance = new DownCastOperator<S,T>();
    public override Expr<S> Inverse(Expr<S> argA, Expr<T> result) {
      return UpCastOperator<T, S>.Instance.Make(result);
    }
  }
  public partial class DownCastOperator1<S, T> : CastOperator<S, T, DownCastOperator1<S, T>> {
    private DownCastOperator1() { }
    public new static readonly DownCastOperator1<S, T> Instance = new DownCastOperator1<S, T>();
    public override Expr<S> Inverse(Expr<S> argA, Expr<T> result) {
      return UpCastOperator1<T, S>.Instance.Make(result);
    }
  }
  public partial class UpCastOperator<S, T> : CastOperator<S, T, UpCastOperator<S, T>> where S : T {
    private UpCastOperator() { }
    public new static readonly UpCastOperator<S, T> Instance = new UpCastOperator<S, T>();
    public override Expr<S> Inverse(Expr<S> argA, Expr<T> result) {
      return DownCastOperator<T, S>.Instance.Make(result);
    }
  }
  public partial class UpCastOperator1<S, T> : CastOperator<S, T, UpCastOperator1<S, T>> {
    private UpCastOperator1() { }
    public new static readonly UpCastOperator1<S, T> Instance = new UpCastOperator1<S, T>();
    public override Expr<S> Inverse(Expr<S> argA, Expr<T> result) {
      return DownCastOperator1<T, S>.Instance.Make(result);
    }
  }
  public partial class ToStringOperator : Operator<object, string, ToStringOperator> {
    public new static readonly ToStringOperator Instance = new ToStringOperator();
    public override SmartString Format(SmartString s) {
      return "ToString" + base.Format(s);
    }
  }
  public static partial class D1Convert<T> {
    public static Expr<Vecs.IVec<T, D1>> DoToVec(Expr<T> e) { return ToVec.Instance.Make(e); }

    public partial class ToVec : Operator<T, Vecs.IVec<T, D1>, ToVec> {
      public new static ToVec Instance = new ToVec();
      private ToVec() { }
      public override Expr<T> Inverse(Expr<T> argA, Expr<Vecs.IVec<T, D1>> result) {
        return ToScalar.Instance.Make(result);
      }
      public override Expr<Bling.Vecs.IVec<T, D1>> Expand(Expr<T> argA) {
        return Vecs.Composite<T, D1>.Make(argA);
      }
      public override Expr<Bling.Vecs.IVec<T, D1>> Make(Expr<T> ArgA) {
        if (ArgA is Operation<Vecs.IVec<T, D1>, T>) {
          var op = (Operation<Vecs.IVec<T, D1>, T>)ArgA;
          if (op.Op == ToScalar.Instance) {
            return op.ArgA;
          }
        }
        return base.Make(ArgA);
      }
    }
    public partial class ToScalar : Operator<Vecs.IVec<T, D1>, T, ToScalar> {
      public new static ToScalar Instance = new ToScalar();
      private ToScalar() { }
      public override Expr<T> Make(Expr<Bling.Vecs.IVec<T, D1>> ArgA) {
        if (ArgA is Operation<T, Vecs.IVec<T, D1>>) {
          var op = (Operation<T, Vecs.IVec<T, D1>>)ArgA;
          if (op.Op == ToVec.Instance) {
            return op.ArgA;
          }
        }
        return base.Make(ArgA);
      }
      public override Expr<T> Expand(Expr<Bling.Vecs.IVec<T, D1>> argA) {
        return Vecs.AccessExpr<T, D1>.Make(argA, 0);
      }
      public override Expr<Bling.Vecs.IVec<T, D1>> Inverse(Expr<Vecs.IVec<T, D1>> argA, Expr<T> result) {
        return ToVec.Instance.Make(result);
      }
    }
  }

  public partial class ExplicitConvertOperator<S, T> : CastOperator<S, T, ExplicitConvertOperator<S, T>> {
    public static Expr<T> Make0(Expr<S> e) { return Instance.Make(e); } 

    private static readonly System.Reflection.MethodInfo ConvertF;
    static ExplicitConvertOperator() {
      foreach (var m in typeof(S).GetMethods()) {
        if (!(m.Name == "op_Explicit") && !(m.Name == "op_Implicit")) continue;
        if (m.GetParameters().Length != 1) continue;
        if (!m.IsStatic) continue;
        if (m.ReturnType != typeof(T)) continue;
        if (m.GetParameters()[0].ParameterType != typeof(S)) continue;
        ConvertF = m;
        break;
      }
    }
    private ExplicitConvertOperator() { }
    public new static readonly ExplicitConvertOperator<S, T> Instance = new ExplicitConvertOperator<S, T>();
    public override Expr<S> Inverse(Expr<S> argA, Expr<T> result) {
      return ExplicitConvertOperator<T, S>.Instance.Make(result);
    }
    public override Expr<T> Make(Expr<S> ArgA) {
      if (typeof(T).IsGenericType && typeof(T).GetGenericTypeDefinition() == typeof(Vecs.IVec<,>)) {
        true.Assert();
      }

      if (ArgA is Operation<T, S>) {
        var op = (Operation<T, S>)ArgA;
        if (op.Op is ExplicitConvertOperator<T, S>) {
          return op.ArgA;
        }
      }
      return base.Make(ArgA);
    }
  }
  public interface IOperation : IExpr {
    IOperator BaseBaseOp { get; }
    Expr this[int Idx] { get; }
  }
  public abstract partial class Operation<S> : Expr<S>, IOperation {
    public abstract IOperator<S> BaseOp { get; }
    public IOperator BaseBaseOp { get { return BaseOp; } }
    public abstract Expr this[int Idx] { get; }
    public abstract Expr<S> Expanded { get; }
    internal Expr<S> Expand0 {
      get {
        var f = Expanded;
        if (f == null) 
          throw new NotSupportedException();
        return f;
      }
    }
    internal static readonly Dictionary<IOperator, Delegate> FCache = new Dictionary<IOperator, Delegate>();
    internal static readonly Dictionary<IOperator, Delegate> GCache = new Dictionary<IOperator, Delegate>();
    internal static TDelegate Find<T, TDelegate>(IBaseOperator<T, TDelegate> op) {
      if (FCache.ContainsKey(op)) return (TDelegate)(object) FCache[op];
      return default(TDelegate);
    }

  }
  public abstract partial class BaseOperation<S, OPR> : Operation<S> where OPR : IOperator<S> {
    public OPR Op { get; internal set; }
    public override IOperator<S> BaseOp { get { return Op; } }
  }


  public partial class Operation<S, T> : BaseOperation<T, IOperatorX<S,T>> {
    public Expr<S> ArgA { get; internal set; }
    public override Expr this[int Idx] {
      get { return ArgA; }
    }
    public override bool IsConstant {
      get {
        return ArgA.IsConstant;
      }
    }
    public override Expr Cheap {
      get {
        if (Op.IsCheap) return ArgA;
        else return base.Cheap;
      }
    }
    protected override string ToString1() {
      return Op.Format(ArgA.ToString()).ToString();
    }
    protected override int GetHashCode0() {
      return Op.GetHashCode() + ArgA.GetHashCode();
    }
    protected override bool Equals0(Expr<T> obj) {
      if (!(obj is Operation<S, T>)) return false;
      var op = (Operation<S, T>)obj;
      return Op.Equals(op.Op) && ArgA.Equals(op.ArgA);
    }
    public override Expr<T> Expanded { get { return Op.Expand(ArgA); } }
    protected override Eval<EVAL>.Info<T> Eval<EVAL>(Eval<EVAL> txt) {
      return Op.Eval(this, txt);
    }
    public override bool Solve(IAssign P, Expr<T> other) {
      Expr<S> newRHS = Op.Inverse(ArgA, other);
      if (newRHS != null && ArgA.Solve(P, newRHS)) return true;
      var f = Expanded;
      return f == null ? false : f.Solve(P, other);
    }
  }
  public partial class Operation<S, T, U> : BaseOperation<U, OperatorX<S, T, U>> {
    public Expr<S> ArgA { get; internal set; }
    public Expr<T> ArgB { get; internal set; }
    public override Expr this[int Idx] {
      get { return Idx == 0 ? ArgA : ((Expr) ArgB); }
    }
    public override bool IsConstant {
      get {
        return ArgA.IsConstant && ArgB.IsConstant;
      }
    }
    public override Expr Cheap {
      get {
        if (Op.IsCheap) {
          if (ArgB.IsConstant) return ArgA;
          else if (ArgA.IsConstant) return ArgB;
        }
        return base.Cheap;
      }
    }
    protected override string ToString1() {
      return Op.Format(ArgA.ToString(), ArgB.ToString()).ToString();
    }
    protected override int GetHashCode0() {
      return Op.GetHashCode() + ArgA.GetHashCode() + ArgB.GetHashCode();
    }
    protected override bool Equals0(Expr<U> obj) {
      if (!(obj is Operation<S, T, U>)) return false;
      var op = (Operation<S, T, U>)obj;
      return Op.Equals(op.Op) && ArgA.Equals(op.ArgA) && ArgB.Equals(op.ArgB);
    }

    public override Expr<U> Expanded { get { return Op.Expand(ArgA, ArgB); } }
    protected override Eval<EVAL>.Info<U> Eval<EVAL>(Eval<EVAL> txt) {
      return Op.Eval(this, txt);
    }
    public override bool Solve(IAssign P, Expr<U> other) {
      {
        Expr<S> newRHS = Op.InverseA(ArgA, ArgB, other);
        if (newRHS != null && ArgA.Solve(P, newRHS)) return true;
      }
      {
        Expr<T> newRHS = Op.InverseB(ArgA, ArgB, other);
        if (newRHS != null && ArgB.Solve(P, newRHS)) return true;
      }
      var f = Expanded;
      return f == null ? false : f.Solve(P, other);
    }
  }


  public partial class Operation<S, T, U, V> : BaseOperation<V, OperatorX<S, T, U, V>> {
    public Expr<S> ArgA { get; internal set; }
    public Expr<T> ArgB { get; internal set; }
    public Expr<U> ArgC { get; internal set; }

    public override Expr Cheap {
      get {
        if (Op.IsCheap) {
          if (ArgA.IsConstant) {
            if (ArgB.IsConstant) return ArgC;
            if (ArgC.IsConstant) return ArgB;
          } else if (ArgB.IsConstant) {
            if (ArgC.IsConstant) return ArgA;
          } 
        }
        return base.Cheap;
      }
    }
    public override bool IsConstant {
      get {
        return ArgA.IsConstant && ArgB.IsConstant && ArgC.IsConstant;
      }
    }
    public override Expr this[int Idx] {
      get { return Idx == 0 ? ArgA : Idx == 1 ? ArgB : ((Expr) ArgC); }
    }
    protected override string ToString1() {
      return Op.Format(ArgA.ToString(), ArgB.ToString(), ArgC.ToString()).ToString();
    }
    protected override int GetHashCode0() {
      return Op.GetHashCode() + ArgA.GetHashCode() + ArgB.GetHashCode() + ArgC.GetHashCode();
    }
    protected override bool Equals0(Expr<V> obj) {
      if (!(obj is Operation<S, T, U, V>)) return false;
      var op = (Operation<S, T, U, V>)obj;
      return Op.Equals(op.Op) && ArgA.Equals(op.ArgA) && ArgB.Equals(op.ArgB) && ArgC.Equals(op.ArgC); ;
    }
    public override Expr<V> Expanded { get { return Op.Expand(ArgA, ArgB, ArgC); } }
    protected override Eval<EVAL>.Info<V> Eval<EVAL>(Eval<EVAL> txt) {
      return Op.Eval(this, txt);
    }
    public override bool Solve(IAssign P, Expr<V> other) {
      {
        Expr<S> newRHS = Op.InverseA(ArgA, ArgB, ArgC, other);
        if (newRHS != null && ArgA.Solve(P, newRHS)) return true;
      }
      {
        Expr<T> newRHS = Op.InverseB(ArgA, ArgB, ArgC, other);
        if (newRHS != null && ArgB.Solve(P, newRHS)) return true;
      }
      {
        Expr<U> newRHS = Op.InverseC(ArgA, ArgB, ArgC, other);
        if (newRHS != null && ArgC.Solve(P, newRHS)) return true;
      }
      var f = Expanded;
      return f == null ? false : f.Solve(P, other);
    }
  }
  public partial class Operation<S, T, U, V, W> : BaseOperation<W, OperatorX<S, T, U, V, W>> {
    public Expr<S> ArgA { get; internal set; }
    public Expr<T> ArgB { get; internal set; }
    public Expr<U> ArgC { get; internal set; }
    public Expr<V> ArgD { get; internal set; }
    public override bool IsConstant {
      get {
        return ArgA.IsConstant && ArgB.IsConstant && ArgC.IsConstant && ArgD.IsConstant;
      }
    }
    public override Expr this[int Idx] {
      get { return Idx == 0 ? ArgA : Idx == 1 ? ArgB : Idx == 2 ? ArgC : ((Expr) ArgD); }
    }
    protected override string ToString1() {
      return Op.Format(ArgA.ToString(), ArgB.ToString(), ArgC.ToString(), ArgD.ToString()).ToString();
    }
    protected override int GetHashCode0() {
      return Op.GetHashCode() + ArgA.GetHashCode() + ArgB.GetHashCode() + ArgC.GetHashCode() + ArgD.GetHashCode();
    }
    protected override bool Equals0(Expr<W> obj) {
      if (!(obj is Operation<S, T, U, V, W>)) return false;
      var op = (Operation<S, T, U, V, W>)obj;
      return Op.Equals(op.Op) && ArgA.Equals(op.ArgA) && ArgB.Equals(op.ArgB) && ArgC.Equals(op.ArgC) && ArgD.Equals(op.ArgD); ;
    }
    public override Expr<W> Expanded { get { return Op.Expand(ArgA, ArgB, ArgC, ArgD); } }
    protected override Eval<EVAL>.Info<W> Eval<EVAL>(Eval<EVAL> txt) {
      return Op.Eval(this, txt);
    }
    public override bool Solve(IAssign P, Expr<W> other) {
      {
        Expr<S> newRHS = Op.InverseA(ArgA, ArgB, ArgC, ArgD, other);
        if (newRHS != null && ArgA.Solve(P, newRHS)) return true;
      }
      {
        Expr<T> newRHS = Op.InverseB(ArgA, ArgB, ArgC, ArgD, other);
        if (newRHS != null && ArgB.Solve(P, newRHS)) return true;
      }
      {
        Expr<U> newRHS = Op.InverseC(ArgA, ArgB, ArgC, ArgD, other);
        if (newRHS != null && ArgC.Solve(P, newRHS)) return true;
      }
      {
        Expr<V> newRHS = Op.InverseD(ArgA, ArgB, ArgC, ArgD, other);
        if (newRHS != null && ArgD.Solve(P, newRHS)) return true;
      }
      var f = Expanded;
      return f == null ? false : f.Solve(P, other);
    }
  }
}
